// 实现这个项目的构建任务

/**
 * 1.安装gulp和gulp-load-plugins到dev，相应任务用到的插件或模块还是需要添加依赖，gulp-load-plugins只是帮我们加载进来对应的依赖，并不会添加相应依赖
 * 2.样式文件的编译转换任务
 * 3.脚本文件的编译转换任务
 * 4.页面文件的编译转换任务，添加一个pages.config.js去配置页面的模板数据，然后读取进来作为转换时使用
 * 5.图片、字体文件、public下的静态资源文件处理任务
 * 6.通过组合任务将他们放在同一个任务下执行
 * 7.创建清除任务用于每次执行构建前自动删除构建目录，不再需要手动操作，将任务同步放在组合任务最前边
 * 8.本地热更新服务任务
 * 9.监视项目文件变化，自动更新编译后更新浏览器
 * 10.调整组合任务，最终导出clean，dev和build任务
 * 11.useref引用文件处理，node_modules相关引用处理，并串行添加到build任务最后
 * 12.文件压缩，在useref任务阶段，涉及临时文件目录，需要对以上的dist做相应调整
 * 13.把命令封装到npm scripts
 *
 * ps：任务通过module.exports导出再通过 yarn gulp [任务名] 执行测试，最后只用导出一个构件任务即可
 * 以上是构建任务基本步骤，详细情况请参考 https://zhuanlan.zhihu.com/p/360782665
 */

const {src, dest, parallel, series, watch} = require('gulp')     // yarn add gulp --dev
const browserSync = require('browser-sync')     // yarn add browser-sync --dev
const loadPlugins = require('gulp-load-plugins')    // yarn add gulp-load-plugins --dev 导入gulp-load-plugins，它是一个方法
const del  = require('del')
const plugins = loadPlugins()
const bs = browserSync.create()
let config = {}


// 样式文件编译任务   yarn add gulp-sass --dev
const style = () => {
  return src('src/assets/styles/*.scss', {base: 'src'})
    .pipe(plugins.sass({outputStyle: 'expanded'}))    // 将sass文件转为css
    .pipe(dest('temp'))
}

// js脚本文件编译任务  yarn add gulp-babel @babel/core @babel/preset-env --dev
const script = () => {
  return src('src/assets/scripts/*.js', {base: 'src'})
    .pipe(plugins.babel({presets: ['@babel/preset-env']}))  // babel转换
    .pipe(dest('temp'))
}

// 页面文件编译任务 yarn add gulp-swig --dev；
const page = () => {
  try {
    const loadConfig = require(`${process.cwd()}/pages.config.js`)
    config = Object.assign({}, config, loadConfig)
  } catch (e) {}

  return src('src/*.html', {base: 'src'})
    .pipe(plugins.swig({data: config.data, defaults: {cache: false}}))
    .pipe(dest('temp'))
}

// 图片处理转换 yarn add gulp-imagemin --dev
const image = () => {
  return src('src/assets/images/**', {base: 'src'})
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

// 字体文件转换
const font = () => {
  return src('src/assets/fonts/**', {base: 'src'})
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

// public下
const extra = () => {
  return src('public/**', {base: 'public'})
    .pipe(dest('dist'))
}

// 清除任务   yarn add del --dev
const clean = () => {
  return del(['dist', 'temp'])  // 注意del不是gulp插件，不能使用plugins
}

// 热更新服务任务    yarn add browser-sync --dev
const serve = () => {
  // 监听以下文件路径的通配符，文件修改后执行对应任务
  watch('src/assets/styles/*.scss', style)
  watch('src/assets/scripts/*.js', script)
  watch('src/*.html', page)
  // watch('src/assets/images/**', image)   // 对于这些静态资源开发阶段的监听意义不大，反而增加了任务开销，在bs中添加baseDir初始化就行
  // watch('src/assets/fonts/**', font)
  // watch('public/**', extra)

  watch([                         // 对于静态资源改变也需要热更新，不需要编译转换，只需要服务刷新就行
    'src/assets/images/**',
    'src/assets/fonts/**',
    'public/**',
  ], bs.reload)

  bs.init({
    notify: false,  // 服务启动时，浏览器右上角的提示关闭
    port: 8080,     // 默认值 3000
    // open: false, // 启动时是否自动打开浏览器，默认值true
    files: 'temp/**',      // 服务器启动后监听的路径通配符，对应的文件修改后就可以热更新
    server: {
      baseDir: ['temp', 'src', 'public'],     // 服务代码的根目录
      routes: {
        '/node_modules': 'node_modules'  // 路径映射，优于baseDir，相关配置找不到再找baseDir
      }
    }
  })
}

// 添加useref任务   yarn add gulp-useref -- dev
// 文件压缩也在此阶段    yarn add gulp-if gulp-htmlmin gulp-clean-css gulp-uglify --dev
const useref = () => {
  return src('temp/*.html', {base: 'temp'})
    .pipe(plugins.useref({searchPath: ['temp', '.']}))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({
      collapseWhitespace: true,
      minifyCSS: true,
      minifyJS: true
    })))     // html压缩
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))     // css压缩
    .pipe(plugins.if(/\.js$/, plugins.uglify()))        // js压缩
    .pipe(dest('dist'))       // 样式、脚本、页面文件压缩涉及文件读写，之前的任务使用temp存放，防止冲突，最终压缩后放入dist
}

// 组合任务
const compile = parallel(style, script, page)      // 编译
const build = series(clean, parallel(compile, image, font, extra), useref)    // 构建
const dev = series(compile, serve)      // 开发阶段 先构建再启动热更新服务

// 最终导出任务
module.exports = {
  clean,
  dev,
  build
}
